package com.spsoft.workflow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.common.collect.Lists;
import com.spsoft.core.enums.ResponseEnum;
import com.spsoft.core.exception.BaseException;
import com.spsoft.core.exception.BusinessException;
import com.spsoft.core.model.Result;
import com.spsoft.core.model.bo.TokenUser;
import com.spsoft.core.model.ProcessParamBo;
import com.spsoft.core.utils.CollectionUtils;
import com.spsoft.core.utils.DateUtils;
import com.spsoft.core.utils.SecurityUtils;
import com.spsoft.core.utils.StringUtil;
import com.spsoft.workflow.common.WorkFlowConstants;
import com.spsoft.workflow.common.cmd.BackUserTaskCmd;
import com.spsoft.workflow.common.enums.AssigneeTypeEnums;
import com.spsoft.workflow.common.enums.BizStatusEnums;
import com.spsoft.workflow.common.enums.BusFlowTypeEnums;
import com.spsoft.workflow.entity.WfProcessBusiness;
import com.spsoft.workflow.entity.WfProcessLog;
import com.spsoft.workflow.entity.WfProcessTemplates;
import com.spsoft.workflow.mapper.WfProcessBusinessMapper;
import com.spsoft.workflow.model.dto.bus.FindApprovalUserDto;
import com.spsoft.workflow.model.dto.bus.FlowDataDto;
import com.spsoft.workflow.model.dto.bus.SaveAndSubmitDto;
import com.spsoft.workflow.model.dto.bus.handlingProcessDto;
import com.spsoft.workflow.model.dto.json.ChildNode;
import com.spsoft.workflow.model.vo.biz.ApplyFlowVo;
import com.spsoft.workflow.model.vo.biz.BaseUserVo;
import com.spsoft.workflow.model.vo.biz.NextHandleInfoVo;
import com.spsoft.workflow.model.vo.biz.TaskVO;
import com.spsoft.workflow.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static com.spsoft.workflow.common.CommonConstants.START_USER_ID;
import static com.spsoft.workflow.common.enums.BizStatusEnums.*;
import static com.spsoft.workflow.common.enums.HandleProcessEnums.*;

@Service
@Slf4j
@RequiredArgsConstructor
public class BusinessFlowServiceImpl implements BusinessFlowService   {

    private final ProcessBusinessService processBusinessService;
    private final WfProcessBusinessMapper wfProcessBusinessMapper;
    private final WfProcessTemplatesService wfProcessTemplatesService;
    private final ProcessEngine processEngine;
    private final RepositoryService repositoryService;
    private final HistoryService historyService;
    private final WfProcessLogService wfProcessLogService ;
    private final RuntimeService runtimeService;
    private final ManagementService managementService;
    private final TaskService taskService;
    @Autowired
    private  Map<String, BusinessHandlerService> handlers;
    /**
     * 新增流程时获取申请数据
     *
     * @param q q 流程编号
     * @return {@link Result}
     */
    @Override
    public Result getFlowData(FlowDataDto q) {
        TokenUser tokenUser = SecurityUtils.getTokenUser();
        if( q.getBusId()==null || q.getBusId()==0) {
            Result result = processBusinessService.newfindApprovalUser(q.getBusType(), tokenUser.getDeptCode());
            if (ResponseEnum.SUCCESS.getCode().equals(result.getCode())) {
                List<BaseUserVo> aUserList = (List<BaseUserVo>) result.getData();
                ApplyFlowVo afvo = new ApplyFlowVo();
                afvo.setCreateTime(new Date());
                afvo.setApplyUsername(tokenUser.getUserName());
                afvo.setApplyUser(tokenUser.getUserCode());
                if (CollectionUtil.isNotEmpty(aUserList)) {
                    afvo.setApprovalUser(aUserList.get(0).getUserCode());
                    afvo.setApprovalUsername(aUserList.get(0).getFullName());
                }
                afvo.setApprovalUserList(aUserList);
                afvo.setBusId(wfProcessBusinessMapper.getNextSequenceValue());
                return Result.OK(afvo);
            }
        }else{
            WfProcessBusiness pbus = processBusinessService.getProcessBusinessByBusId(Convert.toLong(q.getBusId()));
            if(pbus != null){
                ApplyFlowVo afvo = new ApplyFlowVo();
                afvo.setCreateTime(pbus.getCreatedTime());
                afvo.setApplyUsername(pbus.getCreatedByName());
                afvo.setApplyUser(pbus.getCreatedBy());
                afvo.setBusId(pbus.getId());
                //确定当前流程节点 根据节点去获取审批信息
                Task task =null;
                if(StringUtil.isNotEmpty(pbus.getProcInstId())
                        && (BUSINESS_STATUS_1.getStatus().equals(pbus.getBusStatus()) //正在处理
                        || BUSINESS_STATUS_2.getStatus().equals(pbus.getBusStatus()) //撤回
                        || BUSINESS_STATUS_3.getStatus().equals(pbus.getBusStatus()))){//退回
                   task = taskService.createTaskQuery().processInstanceId(pbus.getProcInstId()).singleResult();
                }

                String nodeId =null;
                if(task != null) {
                    nodeId = task.getTaskDefinitionKey();
                    FindApprovalUserDto fauDto = new FindApprovalUserDto();
                    fauDto.setBusId(Convert.toStr(pbus.getId()));
                    fauDto.setProcessInstanceId(pbus.getProcInstId());
                    fauDto.setNodeId(nodeId);
                    Result approvalUser = processBusinessService.findApprovalUser(fauDto);
                    if (ResponseEnum.SUCCESS.getCode().equals(approvalUser.getCode())) {
                        List<BaseUserVo> aUserList = (List<BaseUserVo>) approvalUser.getData();
                        ChildNode node=processBusinessService.getNextNodeId(pbus.getProcessTemplateId(),nodeId,true);
                        if(node !=null){
                            String naxtNodeId = node.getId();
                            //获取流程参数 中获取下一节点审批人
                            Map<String, Object> variables = runtimeService.getVariables(task.getExecutionId());
                            List<String> o = (List<String>) variables.get(naxtNodeId + "assigneeList");
                            if (!CollectionUtils.isAnyEmpty(o)) {
                                afvo.setApprovalUser(o.get(0));
                            }
                        }else{
                            //下一个节点是空， 说是最后一个节点， 取一个即可
                            if(aUserList !=null && aUserList.size()>0){
                                afvo.setApprovalUser(aUserList.get(0).getUserCode());
                            }
                            afvo.setApprovalUser(pbus.getCreatedBy());
                        }
                        afvo.setApprovalUserList(aUserList);
                    }
                }
                //流程说明 读取业务表数据
                afvo.setDescription(pbus.getRemark());
                afvo.setBusData(pbus.getBusData());
                return Result.OK(afvo);
            }
        }

        return Result.ERROR("获取申请信息失败");

    }

    /**
     * 保存或并提交
     *
     * @param s s
     * @return {@link Result}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result saveOrSubmit(SaveAndSubmitDto s) {
        WfProcessBusiness wfpb = null;
        TokenUser user = SecurityUtils.getTokenUser();
        BusFlowTypeEnums busFlowTypeEnums = BusFlowTypeEnums.valueOf(s.getBusType());
        WfProcessTemplates template = wfProcessTemplatesService.getFormTemplateByBusType(busFlowTypeEnums);
        if(template == null){
            return Result.ERROR("流程模板不存在");
        }

        Long busId =  s.getBusId();
        if(busId !=null && busId !=0){
            wfpb = processBusinessService.getById(busId);

            if(wfpb !=null && (BUSINESS_STATUS_5.getStatus().equals(wfpb.getBusStatus())
                    ||BUSINESS_STATUS_4.getStatus().equals(wfpb.getBusStatus())) ){
                return Result.ERROR("业务流程状态不正确");
            }
            if(wfpb == null){
                wfpb=new WfProcessBusiness();
                wfpb.setId(s.getBusId());
                wfpb.setBusCode(BusFlowTypeEnums.getFlowCode(busFlowTypeEnums));
                wfpb.setCreatedBy(user.getUserCode());
                wfpb.setCreatedTime(DateUtils.parseDate2(s.getCreateTime(),DateUtils.NORM_DATETIME_PATTERN ));
                wfpb.setBusStatus(BUSINESS_STATUS_1.getStatus());
            }
            if(wfpb !=null && wfpb.getProcInstId() !=null){
                Task task = taskService.createTaskQuery().processInstanceId(wfpb.getProcInstId()).singleResult();
                if(task != null &&  !"root".equals(task.getTaskDefinitionKey())){
                   // 流程已经提交， 不能修改
                    return Result.ERROR("该流程已经提交，不能重复提交，请重新查询查看状态");
                }
            }
            wfpb.setUpdatedBy(user.getUserCode());
            wfpb.setUpdatedTime(new Date());
        }else{
           return Result.ERROR("业务流程ID不能为空");
        }
        wfpb.setBusName(BusFlowTypeEnums.getFlowName(s.getBusType(),user.getUserName()));
        wfpb.setProcessTemplateId(template.getId());
        wfpb.setRemark(s.getDescription());
        wfpb.setDeptCode(user.getDeptCode());
        wfpb.setBusType(s.getBusType());

        wfpb.setBusData(s.getBusData());
        if(!processBusinessService.saveOrUpdate(wfpb)){
            return Result.ERROR("业务流程保存失败");
        };
        busId = wfpb.getId();
        //获取到流程节点配置
        ChildNode node=processBusinessService.getNextNodeId(template.getId(),"root",true);
        String naxtNodeId = node.getId();
        if(!AssigneeTypeEnums.ROLE.getTypeName().equals(node.getProps().getAssignedType())) {
           throw new BusinessException(ResponseEnum.PROCESS_NODE_NOTSUPPORTED_ERROR);
        }
        List<Map<String, Object>> roles = node.getProps().getRole();
        if(CollUtil.isEmpty(roles)){
            throw new BusinessException(ResponseEnum.PROCESS_NODE_NOTSUPPORTED_ERROR);
        }

        String roleName = "";
        for (Map<String, Object> role : roles) {
            roleName = Convert.toStr(role.get("roleName"));
            roleName+=",";
        }

        //设置审核人员
        Map<String, Object> processVariables = new HashMap<>();
        List<String> arrrovalUserList = Lists.newArrayList();
        arrrovalUserList.add(s.getApprovalUser());
        processVariables.put(naxtNodeId+"assigneeList",arrrovalUserList);
        processVariables.put(START_USER_ID,user.getUserCode());

        String ProcessInstanceId= null;
        String ProcessDefinitionId= null;
        if(StringUtil.isEmpty(wfpb.getProcDefId())){
            ProcessInstanceBuilder processInstanceBuilder = processEngine.getRuntimeService().createProcessInstanceBuilder();
            ProcessInstance processInstance = processInstanceBuilder
                    .processDefinitionKey(WorkFlowConstants.PROCESS_PREFIX + template.getId())
                    .variables(processVariables)
                    .businessKey(Convert.toStr(busId))
                    .start();
            ProcessInstanceId= processInstance.getProcessInstanceId();
            ProcessDefinitionId= processInstance.getProcessDefinitionId();
            wfpb.setProcInstId(ProcessInstanceId);
            wfpb.setProcDefId(ProcessDefinitionId);
        }
        if(s.getSubmit()) {//如果提交 不提交不填写
            wfpb.setRoleNames(roleName.substring(0,roleName.length()-1));
            wfpb.setBusStatus(BUSINESS_STATUS_1.getStatus());
        }else{
            wfpb.setRoleNames(user.getRoleName());
        }
        boolean b = processBusinessService.updateById(wfpb);
        if(b) {
            if(s.getSubmit()) {

                BusinessHandlerService handlerService = handlers.get(s.getBusType());
                if(handlerService == null){
                    throw new BusinessException(ResponseEnum.PROCESS_NOT_ERROR);
                }else{
                    try {
                        ProcessParamBo pp = ProcessParamBo.builder()
                                .busId(busId)
                                .busData(s.getBusData())
                                .nodeId("root")
                                .submit(s.getSubmit())
                                .build();
                        handlerService.processHandler(pp);
                    } catch (Exception e) {
                        throw e;
                    }
                }
                //发起流程后默认完成第一个节点任务
                Task firstTask = taskService.createTaskQuery().processInstanceId(wfpb.getProcInstId()).singleResult();
                taskService.complete(firstTask.getId(),processVariables);
                //添加流程记录
                BpmnModel bpmnModel = repositoryService.getBpmnModel(wfpb.getProcDefId());
                String rootName = bpmnModel.getMainProcess().getFlowElement("root").getName();
                WfProcessLog processLog = new WfProcessLog();
                processLog.setTaskId(firstTask.getId());
                processLog.setBusId(wfpb.getId());
                processLog.setHandleResult(HANDLE_RESULT_0.getDescription());
                processLog.setRemark(s.getDescription());
                processLog.setCreatedBy(user.getUserCode());
                processLog.setCreatedTime(new Date());
                processLog.setProcInstId(wfpb.getProcInstId());
                processLog.setNodeId("root");
                processLog.setNodeName(rootName);
                processLog.setDeptCode(user.getDeptCode());
                processLog.setRolename(user.getRoleName());
                processLog.setRoleid(user.getRoleId());
                wfProcessLogService.save(processLog);
            }
            return Result.OK();
        }else{
            return Result.ERROR("业务流程保存失败");
        }

    }

    /**
     * 处理过程
     *
     * @param h h
     * @return {@link Result}
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result handlingProcess(handlingProcessDto h) {
        TokenUser user = SecurityUtils.getTokenUser();
        WfProcessBusiness wfpb = processBusinessService.getById(h.getBusId());
        if(wfpb == null){
            return Result.ERROR("业务流程不存在");
        }
        if (BUSINESS_STATUS_4.getStatus().equals(wfpb.getBusStatus())
                || BUSINESS_STATUS_5.getStatus().equals(wfpb.getBusStatus())
                || BUSINESS_STATUS_6.getStatus().equals(wfpb.getBusStatus())
        ) {
            return Result.ERROR("业务流程状态不正确");
        }

        Task task = taskService.createTaskQuery().processInstanceId(wfpb.getProcInstId()).taskAssignee(user.getUserCode()).singleResult();
        if(task == null){
            return Result.ERROR("未找到当前用户处理的任务");
        }
        String remark = h.getRemark();
        String taskId = task.getId();
        String taskDefinitionKey = task.getTaskDefinitionKey();
        WfProcessLog processLog = new WfProcessLog();
        //流程通过
        if (HANDLE_RESULT_1.getStatus().equals(h.getHandleResult())) {
            //执行业务处理，无异常则通过
            ProcessParamBo pp = ProcessParamBo.builder()
                    .busId(wfpb.getId())
                    .busData(wfpb.getBusData())
                    .nodeId(taskDefinitionKey)
                    .build();

            handlers.get(wfpb.getBusType()).processHandler(pp);
            //审批通过
            processBusinessService.approved(wfpb,h.getNextHandleUser(),user,task);
            processLog.setHandleResult(HANDLE_RESULT_1.getDescription());
            //退回 默认退回上一节点
        }else if (HANDLE_RESULT_3.getStatus().equals(h.getHandleResult())) {
            List<TaskVO> returnTaskList = processBusinessService.findReturnTaskList(taskId);
            if (CollUtil.isEmpty(returnTaskList)) {
                throw new BaseException("没有可退回的审批节点");
            }
            //获取退回节点配置
            ChildNode flowNodeConfig = processBusinessService.getFlowNodeConfig(taskId);
            Map<String, Object> refuse = flowNodeConfig.getProps().getRefuse();
            String type = Convert.toStr(refuse.get("type"));

            if ("TO_BEFORE".equals(type)) {
//                退回到上级审批节点
                String lastNodeCanBack = returnTaskList.get(0).getId();
                String roleName = null;
                if("root".equals(lastNodeCanBack)){
                   List<WfProcessLog> list = wfProcessLogService.list(new LambdaQueryWrapper<WfProcessLog>()
                            .eq(WfProcessLog::getProcInstId, wfpb.getProcInstId())
                            .eq(WfProcessLog::getNodeId, "root")
                            .orderByDesc(WfProcessLog::getId)
                   );
                    WfProcessLog one =new WfProcessLog();
                    if(list != null && list.size() > 0){
                       one = list.get(0);
                    }
                    roleName = one.getRolename();
                }else{
                    ChildNode node=processBusinessService.getNextNodeId(wfpb.getProcessTemplateId(),lastNodeCanBack,false);
                    //获取到下一个环节的角色信息
                    List<Map<String, Object>> roles = node.getProps().getRole();
                    if(CollUtil.isNotEmpty(roles)){
                        for (Map<String, Object> role : roles) {
                            roleName = Convert.toStr(role.get("roleName"));
                            roleName+=",";
                        }
                    }
                    roleName.substring(0,roleName.length()-1);
                }
                //更新业务状态
                processBusinessService.updateBizStatus(task.getProcessInstanceId(), BUSINESS_STATUS_3.getStatus(),roleName);

                managementService.executeCommand(new BackUserTaskCmd(taskId, lastNodeCanBack));
            } else {
                throw new BaseException("退回类型不正确,请联系管理员");
            }
            processLog.setHandleResult(HANDLE_RESULT_3.getDescription());

        }
        processLog.setBusId(Convert.toLong(wfpb.getId()));
        processLog.setTaskId(taskId);
        processLog.setRemark(remark);
        processLog.setCreatedBy(user.getUserCode());
        processLog.setCreatedTime(new Date());
        processLog.setProcInstId(wfpb.getProcInstId());
        processLog.setNodeId(taskDefinitionKey);
        processLog.setNodeName(task.getName());
        processLog.setDeptCode(wfpb.getDeptCode());
        processLog.setRolename(user.getRoleName() );
        processLog.setRoleid(user.getRoleId());
        wfProcessLogService.save(processLog);
        return Result.OK();
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result stopProcess(Long busId) {
        TokenUser user = SecurityUtils.getTokenUser();
        WfProcessBusiness byId = processBusinessService.getById(busId);
        if(byId == null){
            return Result.ERROR("业务流程不存在");
        }
        if (BUSINESS_STATUS_4.getStatus().equals(byId.getBusStatus())
                || BUSINESS_STATUS_5.getStatus().equals(byId.getBusStatus())
        ) {
            return Result.ERROR("业务流程状态已作废或已结束");
        }

        BusinessHandlerService handlerService = handlers.get(byId.getBusType());
        if(handlerService == null){
            throw new BusinessException(ResponseEnum.PROCESS_NOT_ERROR);
        }else{
            if(!handlerService.stop(byId.getId())){
                throw new BusinessException(ResponseEnum.PROCESS_NODE_ERROR);
            };
        }

        if(!BUSINESS_STATUS_6.getStatus().equals(byId.getBusStatus())){
            String processInstanceId = byId.getProcInstId();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if (null != processInstance) {
                runtimeService.deleteProcessInstance(processInstanceId, "流程作废");
            }
            historyService.deleteHistoricProcessInstance(processInstanceId);
        }
        LambdaUpdateWrapper<WfProcessBusiness> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(WfProcessBusiness::getBusStatus, BizStatusEnums.BUSINESS_STATUS_5.getStatus());
        updateWrapper.set(WfProcessBusiness::getRoleNames,"");
        updateWrapper.set(WfProcessBusiness::getProcInstId,"");
        updateWrapper.eq(WfProcessBusiness::getId, byId.getId());
        if(processBusinessService.update(updateWrapper)){
            WfProcessLog processLog = new WfProcessLog();
            processLog.setTaskId("");
            processLog.setBusId(byId.getId());
            processLog.setHandleResult(HANDLE_RESULT_4.getDescription());
            processLog.setRemark("流程作废");
            processLog.setCreatedBy(user.getUserCode());
            processLog.setCreatedTime(new Date());
            processLog.setDeptCode(byId.getDeptCode());
            processLog.setRolename(user.getRoleName());
            processLog.setRoleid(user.getRoleId());
            wfProcessLogService.save(processLog);
        };

        return Result.OK();
    }

    @Override
    public Result getNextHandleInfo(Long busId) {

        WfProcessBusiness pbus = processBusinessService.getById(busId);
        if(pbus != null) {

            String processInstanceId = pbus.getProcInstId();
            Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
            if (null == task) {
                return Result.ERROR("流程已结束");
            }
            String nodeId = task.getTaskDefinitionKey();
            ChildNode nextNode = processBusinessService.getNextNodeId(pbus.getProcessTemplateId(), nodeId, true);
            NextHandleInfoVo infoVo = new NextHandleInfoVo();
            if(nextNode != null){
                infoVo.setNextNodeName(nextNode.getName());
            }else{
                //未找到下一节点信息
                infoVo.setNextNodeName("流程结束");
                BaseUserVo vo = new BaseUserVo();
                vo.setFullName("无");
                vo.setUserCode("");
                infoVo.setHandleUserList(Arrays.asList(vo));
                return Result.OK(infoVo);
            }
            FindApprovalUserDto fudto = new FindApprovalUserDto();
            fudto.setBusId(Convert.toStr(busId));
            fudto.setProcessInstanceId(pbus.getProcInstId());
            fudto.setNodeId(nodeId);
            Result approvalUser = processBusinessService.findApprovalUser(fudto);
            if(ResponseEnum.SUCCESS.getCode().equals(approvalUser.getCode())){
                infoVo.setHandleUserList((List<BaseUserVo>) approvalUser.getData());
            }
            return Result.OK(infoVo);
        }
        return Result.ERROR("未找到流程信息");
    }


}
